package com.dd.ms.auth.config.auth;

import com.dd.ms.common.exception.DomainException;
import com.dd.ms.common.pojo.annotation.Attention;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;

import javax.crypto.SecretKey;
import java.util.Base64;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author liuxianmeng
 * @CreateTime 2025/7/8 15:44
 * @Description JWT工具类 - 负责令牌的生成和解析
 *
 * 功能说明：
 * 1. 使用HS512算法和密钥生成JWT
 * 2. 将用户名和角色信息嵌入令牌
 * 3. 设置令牌有效期
 * 4. 解析并验证传入的JWT
 *
 * 配置参数：
 * - jwt.secret: 加密密钥（至少256位）
 * - jwt.expiration: 令牌有效期（毫秒）
 */
@Service
public class JwtUtils {

    //@Value("${jwt.access-expiration}")
    private final long accessExpiration = 900000 * 4 * 24; // 过期时间 15分钟
    //private final long accessExpiration = 10000; // 过期时间 15分钟

    @Attention("签发者标识（与验证时的requireIssuer一致）")
    @Value("${spring.application.name}")
    private String jwtIssuer;

    // 使用更安全的算法
    private static final SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS512;

    // 密钥对象（避免重复计算）
    private SecretKey secretKey;

    // Token黑名单（生产环境应替换为Redis）
    //private final ConcurrentHashMap<String, Long> tokenBlacklist = new ConcurrentHashMap<>();

    // 初始化密钥
    private SecretKey getSecretKey() {
        if (secretKey == null) {
            //@Value("${jwt.secret}")
            String base64Secret = "S+fiwuWQORY7ScLeyf2iX+F7sl9Xy9WfkchQhUOdQLd+a0FAEbvUt0/zoPHhNOfZs2x4uZ4ed+OdY0ujuJ+c5g==";
            byte[] decodedKey = Base64.getDecoder().decode(base64Secret);
            secretKey = Keys.hmacShaKeyFor(decodedKey);
        }
        return secretKey;
    }

    public String generateAccessToken(String account, List<String> roles) {
        return Jwts.builder()
                .setIssuer(jwtIssuer) // 关键添加：设置签发者
                .setSubject(account)
                .claim("typ", "ACCESS")
                .claim("roles", roles)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + accessExpiration))
                .signWith(getSecretKey(), SIGNATURE_ALGORITHM)
                .compact();
    }

    public String generateRefreshToken(String account) {
        //@Value("${jwt.refresh-expiration}")
        long refreshExpiration = 2592000000L; // 30天
        return Jwts.builder()
                .setIssuer(jwtIssuer) // 关键添加：设置签发者
                .setSubject(account)
                .claim("typ", "REFRESH")
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + refreshExpiration))
                .signWith(getSecretKey(), SIGNATURE_ALGORITHM)
                .compact();
    }

    /**
     * 根据token获取账户名
     *
     * @param token accessToken或refreshToken
     * @return 账户名
     */
    public String getAccountFromToken(String token) {
        try {
            return parseToken(token).getSubject();
        } catch (ExpiredJwtException e) {
            // 特殊处理：即使过期也能获取用户名（用于刷新流程）
            return e.getClaims().getSubject();
        } catch (MalformedJwtException e) {
            throw new DomainException(401, "无效的Token格式");
        } catch (JwtException | IllegalArgumentException e) {
            throw new DomainException(401, "无效的Token");
        }
    }

    public Claims parseToken(String token) throws DomainException {
        // 1. 空值检查
        if (token == null || token.isEmpty()) {
            throw new IllegalArgumentException("Token不能为空");
        }

        // 2. 检查黑名单
        //if (tokenBlacklist.containsKey(token)) {
        //    throw new ExpiredJwtException(null, null, "Token已被撤销");
        //}

        // 3. 解析Token
        Claims claims;
        try {
            claims = Jwts.parserBuilder()
                    .setSigningKey(getSecretKey())
                    .requireIssuer(jwtIssuer) // 签发者验证
                    .build()
                    .parseClaimsJws(token)
                    .getBody();
        } catch (ExpiredJwtException e) {
            // 特殊处理：返回过期但有效的claims
            throw new DomainException(401, "Token过期或不合法");
        }

        // 4. 验证Token类型
        String tokenType = claims.get("typ", String.class);
        if (!("ACCESS".equals(tokenType) || "REFRESH".equals(tokenType))) {
            throw new MalformedJwtException("无效的Token类型");
        }

        return claims;
    }

    public boolean validateRefreshToken(String refreshToken) {
        try {
            Claims claims = parseToken(refreshToken);
            // 额外检查Token类型
            return "REFRESH".equals(claims.get("typ"));
        } catch (DomainException e) {
            throw new DomainException(401002, "refreshToken已过期，请重新登陆");
        }
    }

    public boolean validateAccessToken(String accessToken) {
        try {
            Claims claims = parseToken(accessToken);
            // 额外检查Token类型
            return "ACCESS".equals(claims.get("typ"));
        } catch (DomainException e) {
            throw new DomainException(401001, "accessToken已过期，请刷次accessToken");
        }
    }

    public long getExpirationFromToken(String token) {
        return parseToken(token).getExpiration().getTime();
    }

    public Long getAccessTokenExpireSeconds() {
        return accessExpiration / 1000;
    }
}